/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Controller;

import Model.TrafficLight;
import Model.StreetBlock;
import Model.Const;
import Model.Car;
import Model.IntersectionInfo;

/**
 * Traffic class to process and control traffic flow based on the values entered by user.
 * @author Chiaki Sato
 * @since J2SDK 1.6
 * @version 1.0
 */
public class Traffic {

    // private instance valiables
    private int nLaneHoriz;
    private int nLaneVert;
    private float fProbabilityHoriz;
    private float fProbabilityVert;
    private TrafficLight oTraficLight;
    private String stCarDrawString;
    private String[][] arrayCarDrawVretical;
    private int nRoadLenghtHoriz;
    private int nRoadLenghtVert;
    private int nRound;
    private String stRoadLineHoriz;
    private int nTrafficLightStatusHoriz;
    private int nTrafficLightStatusVert;
    private String stTrafficLight;
    private StreetBlock[][] StreetBlockHoriz;
    private StreetBlock[][] StreetBlockVert;

    /**
     * Constructor of traffic class that initializes instance variables based on values selected from menu by user.
     * @param oMainMenu Main menu class object
     */
    public Traffic(IntersectionInfo oIntersectionInfo){

        int i = 0;

        // init private instance variables
        stCarDrawString = "";
        nRoadLenghtHoriz = oIntersectionInfo.getRoadLenghtHoriz();
        nRoadLenghtVert = oIntersectionInfo.getRoadLenghtVert();
        nRound = 0;
        stRoadLineHoriz ="";
        nTrafficLightStatusHoriz = Const.LIGHT_GREEN;
        nTrafficLightStatusVert = Const.LIGHT_RED;
        stTrafficLight = "";
        nLaneHoriz = oIntersectionInfo.getLaneHoriz();
        nLaneVert = oIntersectionInfo.getLaneVert();
        fProbabilityHoriz = oIntersectionInfo.getProbabilityHoriz();
        fProbabilityVert = oIntersectionInfo.getProbabilityVert();

        StreetBlockHoriz = new StreetBlock[nLaneHoriz][Const.ROAD_BLOCKS];
        StreetBlockVert = new StreetBlock[nLaneVert][Const.ROAD_BLOCKS];

        // create traffic light object
        oTraficLight = new TrafficLight(nLaneVert);

        for (i=0; i<nLaneHoriz; i++){

            StreetBlock streetBlock2 = new StreetBlock(null, nRoadLenghtHoriz, Const.SOUTH_EAST, fProbabilityHoriz);
            StreetBlock streetBlock1 = new StreetBlock(streetBlock2, nRoadLenghtHoriz, Const.NORTH_WEST, fProbabilityHoriz);
            StreetBlockHoriz[i][Const.EAST] = streetBlock2;
            StreetBlockHoriz[i][Const.WEST] = streetBlock1;
        }
        for (i=0; i<nLaneVert; i++){

            StreetBlock streetBlock2 = new StreetBlock(null, nRoadLenghtVert, Const.SOUTH_EAST, fProbabilityVert);
            StreetBlock streetBlock1 = new StreetBlock(streetBlock2, nRoadLenghtVert, Const.NORTH_WEST, fProbabilityVert);
            StreetBlockVert[i][Const.SOUTH] = streetBlock2;
            StreetBlockVert[i][Const.NORTH] = streetBlock1;
        }

        stRoadLineHoriz = "";
        for (i=0; i<nRoadLenghtHoriz; i++){
            stRoadLineHoriz += Const.SYMBOL_STREET_BOUNDARIES_HORIZ;
        }
    }

    /**
     * Public function to process one or multiple traffic flow and display the traffic situation with each cycle.
     * @param nRoundMax Maximum iteration of traffic flow
     */
    public String doTrafficFlow(int nRoundMax) {

        int i, k, m;
        String stResultDrawString = "";

        for (nRound=0; nRound < nRoundMax; nRound++){

            oTraficLight.changeTrafficLightColor();
            nTrafficLightStatusHoriz = oTraficLight.getfTraficLightHoriz();
            nTrafficLightStatusVert = oTraficLight.getfTraficLightVert();

            arrayCarDrawVretical = new String[nRoadLenghtVert][Const.ROAD_BLOCKS];

            // moves cas vertically on street blocks for all lanes
            for (i=0; i<nLaneVert; i++){

                StreetBlockVert[i][Const.SOUTH].moveCarsOnStreetBlock(this, i, Const.STREET_POSITION_SOUTH, Const.LIGHT_NONE);
                StreetBlockVert[i][Const.NORTH].moveCarsOnStreetBlock(this, i, Const.STREET_POSITION_NORTH, nTrafficLightStatusVert);
            }

            // to support overtake, getCarsDrawString shoul be called after complation of moveCarsOnStreetBlock process loop for all lanes
            for (i=0; i<nLaneVert; i++){

                arrayCarDrawVretical[i][Const.SOUTH] = StreetBlockVert[i][Const.SOUTH].getCarsDrawString();
                arrayCarDrawVretical[i][Const.NORTH] = StreetBlockVert[i][Const.NORTH].getCarsDrawString();
            }

            for (i=0; i<nRoadLenghtVert; i++){

                stCarDrawString = "";

                // left margin of vertical street
                for (k=0; k<nRoadLenghtHoriz; k++){
                    stCarDrawString += " ";
                }

                // create a daraw string with car for vertical street
                for (m=0; m<nLaneVert; m++){
                    stCarDrawString += Const.SYMBOL_STREET_BOUNDARIES_VERT;
                    stCarDrawString += arrayCarDrawVretical[m][Const.NORTH].charAt(i);
                }
                stCarDrawString += Const.SYMBOL_STREET_BOUNDARIES_VERT;

                // Daraw one length of vertical street
                stResultDrawString += stCarDrawString + '\n';
            }

            // moves cas horizontally on street blocks for all lanes
            for (i=0; i<nLaneHoriz; i++){

                StreetBlockHoriz[i][Const.EAST].moveCarsOnStreetBlock(this, i, Const.STREET_POSITION_EAST ,Const.LIGHT_NONE);
                StreetBlockHoriz[i][Const.WEST].moveCarsOnStreetBlock(this, i, Const.STREET_POSITION_WEST, nTrafficLightStatusHoriz);
            }

            // to support overtake, getCarsDrawString should be called after completion of moveCarsOnStreetBlock process loop for all lanes
            for (i=0; i<nLaneHoriz; i++){

                if (i==0){
                    stTrafficLight = oTraficLight.getTrafficLightStringPatternTop();
                }
                else{
                    stTrafficLight = oTraficLight.getTrafficLightStringPatternBottom();
                }
//                System.out.println(stRoadLineHoriz + stTrafficLight + stRoadLineHoriz);
                stResultDrawString += stRoadLineHoriz + stTrafficLight + stRoadLineHoriz + '\n';


                stTrafficLight = oTraficLight.getTrafficLightStringPatternBottom();
                stCarDrawString = StreetBlockHoriz[i][Const.WEST].getCarsDrawString() + stTrafficLight + StreetBlockHoriz[i][Const.EAST].getCarsDrawString();

                stResultDrawString += stCarDrawString + '\n';

            }
            stResultDrawString += stRoadLineHoriz + stTrafficLight + stRoadLineHoriz + '\n';

            for (i=0; i<nRoadLenghtVert; i++){

                stCarDrawString = "";

                // left margin of vertical street
                for (k=0; k<nRoadLenghtHoriz; k++){
                    stCarDrawString += " ";
                }

                // create a daraw string with car for vertical street
                for (m=0; m<nLaneVert; m++){
                    stCarDrawString += Const.SYMBOL_STREET_BOUNDARIES_VERT;
                    stCarDrawString += arrayCarDrawVretical[m][Const.SOUTH].charAt(i);
                }
                stCarDrawString += Const.SYMBOL_STREET_BOUNDARIES_VERT;

                // Daraw one length of vertical street
                stResultDrawString += stCarDrawString + '\n';
            }
//            System.out.println("");
            stResultDrawString += '\n';
        }

        return stResultDrawString;
    }


    /**
     * Public function to check lanes for overtaking and invoke overtaking process.
     * @param oOvertakingCar Overtaking car class object
     * @param nPosCar Original position of overtaking car
     * @param nLane Lane number overtaking car running
     * @param nStreetLocation Location of street block overtaking car running
     * @return Result of overtaking process
     */
    public boolean tryOvertake(Car oOvertakingCar, int nPosCar, int nLane, int nStreetLocation){

        boolean isOvertakeSuccess = false;

        if (nStreetLocation == Const.STREET_POSITION_NORTH){

            // check if one side lane exist
            if (nLane-1 >= 0){

                // challenge overtake
                isOvertakeSuccess = StreetBlockVert[nLane-1][Const.NORTH].overtake(oOvertakingCar, nPosCar);
            }
            // check if another side lane exist
            if (nLane+1 < nLaneVert && isOvertakeSuccess == false){

                // challenge overtake
                isOvertakeSuccess = StreetBlockVert[nLane+1][Const.NORTH].overtake(oOvertakingCar, nPosCar);
            }
        }
        else if(nStreetLocation == Const.STREET_POSITION_SOUTH)
        {

            // check if one side lane exist
            if (nLane-1 > 0){

                // challenge overtake
                isOvertakeSuccess = StreetBlockVert[nLane-1][Const.SOUTH].overtake(oOvertakingCar, nPosCar);
            }
            // check if another side lane exist
            else if (nLane+1 < nLaneVert && isOvertakeSuccess == false){

                // challenge overtake
                isOvertakeSuccess = StreetBlockVert[nLane+1][Const.SOUTH].overtake(oOvertakingCar,nPosCar);
            }
        }
        else if(nStreetLocation == Const.STREET_POSITION_WEST)
        {

            // check if one side lane exist
            if (nLane-1 > 0){

                // challenge overtake
                isOvertakeSuccess = StreetBlockHoriz[nLane-1][Const.WEST].overtake(oOvertakingCar, nPosCar);
            }
            // check if another side lane exist
            else if (nLane+1 < nLaneHoriz && isOvertakeSuccess == false){

                // challenge overtake
                isOvertakeSuccess = StreetBlockHoriz[nLane+1][Const.WEST].overtake(oOvertakingCar,nPosCar);
            }
        }
        else if(nStreetLocation == Const.STREET_POSITION_EAST)
        {

            // check if one side lane exist
            if (nLane-1 > 0){

                // challenge overtake
                isOvertakeSuccess = StreetBlockHoriz[nLane-1][Const.EAST].overtake(oOvertakingCar, nPosCar);
            }
            // check if another side lane exist
            else if (nLane+1 < nLaneHoriz && isOvertakeSuccess == false){

                // challenge overtake
                isOvertakeSuccess = StreetBlockHoriz[nLane+1][Const.EAST].overtake(oOvertakingCar,nPosCar);
            }
        }

        return isOvertakeSuccess;
    }

    public Car getCarOnStreet(int nLane, int nstreetPosition, int nPosCar){

        Car oCar = null;

        if (nstreetPosition == Const.STREET_POSITION_NORTH){
            oCar = StreetBlockVert[nLane][Const.NORTH].getCarOnStreet(nPosCar);
        }
        else if (nstreetPosition == Const.STREET_POSITION_SOUTH){
            oCar = StreetBlockVert[nLane][Const.SOUTH].getCarOnStreet(nPosCar);
        }
        else if (nstreetPosition == Const.STREET_POSITION_EAST){
            oCar = StreetBlockHoriz[nLane][Const.EAST].getCarOnStreet(nPosCar);
        }
        else if (nstreetPosition == Const.STREET_POSITION_WEST){
            oCar = StreetBlockHoriz[nLane][Const.WEST].getCarOnStreet(nPosCar);
        }

        return oCar;
    }

    /**
    * Public function to get a value of instance variable.
    * @return Horizontal traffic light status
    */
    public int getfTraficLightHoriz() {

        return oTraficLight.getfTraficLightHoriz();
    }

    /**
    * Public function to get a value of instance variable.
    * @return Vertical traffic light status
    */
    public int getfTraficLightVert() {

        return oTraficLight.getfTraficLightVert();
    }
}

